/*
 * Copyright 2011 Daniel Krieg
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.code.csf.locators.ejb;

import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;

public class RemoteSlsbServiceLocator<ServiceType> extends LocalSlsbServiceLocator<ServiceType> {
    private Class homeInterface;
    private boolean cacheSessionBean = false;
    private ServiceType beanInstance;
    private final Object beanInstanceMonitor = new Object();

    public RemoteSlsbServiceLocator() {
    }

    public RemoteSlsbServiceLocator(Properties environment) {
        super(environment);
    }

    /**
     * Set whether to cache the actual session bean object.
     * <p>Off by default for standard EJB compliance. Turn this flag
     * on to optimize session bean access for servers that are
     * known to allow for caching the actual session bean object.
     * @see #setCacheHome
     */
    public final void setCacheSessionBean(boolean cacheSessionBean) {
        this.cacheSessionBean = cacheSessionBean;
    }

    public void setHomeInterface(Class homeInterface) {
        if (homeInterface != null && !homeInterface.isInterface()) {
            throw new IllegalArgumentException(
                    "Home interface class [" + homeInterface.getClass() + "] is not an interface");
        }
        this.homeInterface = homeInterface;
    }

    @Override
    protected Class<?> getHomeClass() throws ClassNotFoundException {
        return Class.forName("javax.ejb.EJBHome");
    }

    @Override
    public Object lookup() throws NamingException {
        Object homeObject = super.lookup();
        if (homeInterface != null) {
            try {
                homeObject = PortableRemoteObject.narrow(homeObject, homeInterface);
            }
            catch (ClassCastException ex) {
                throw new RuntimeException(
                        "Could not narrow EJB home stub to home interface [" + homeInterface.getName() + "]", ex);
            }
        }
        return homeObject;
    }

    @Override
    protected ServiceType getSessionBeanInstance() throws NamingException, InvocationTargetException {
        if (cacheSessionBean) {
            synchronized (beanInstanceMonitor) {
                if (beanInstance == null) {
                    beanInstance = newSessionBeanInstance();
                }
                return beanInstance;
            }
        }
        else {
            return newSessionBeanInstance();
        }
    }

    @Override
    protected void removeSessionBeanInstance(ServiceType ejb) {
        if (!cacheSessionBean) {
            super.removeSessionBeanInstance(ejb);
        }
    }

    @Override
    protected void refreshHome() throws NamingException {
        super.refreshHome();
        if (cacheSessionBean) {
            synchronized (beanInstanceMonitor) {
                beanInstance = null;
            }
        }
    }
}
